package com.jqj;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;

import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

public class AopAgentTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className,
                            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) {
//        System.out.printf("\033[31;1mtransform0 ===>> %s\033[0m\n", className);
        if (!className.startsWith("com/tongmmp/cnmp/controller/")) return classfileBuffer;

        byte[] transformed = null;
        ClassPool pool;
        CtClass ctClass = null;
        try {
            System.out.printf("\033[32;1mtransform1 ===>> %s\033[0m\n", className);
            pool = ClassPool.getDefault();
            ctClass = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));

//            CtMethod aop_method = pool.get("com.jdktest.instrument.AopMethods").
//                    getDeclaredMethod("aopMethod");
//            System.out.println(aop_method.getLongName());

            if (!ctClass.isInterface()) {
                CtMethod[] methods = ctClass.getDeclaredMethods();
                for (CtMethod method : methods) {
                    if (!method.isEmpty()) {
                        System.out.printf("\033[32;1mAOPInsertMethod ===>> %s#%s\033[0m\n", ctClass.getName(), method.getName());
                        AOPInsertMethod(method);
                    }
                }
                transformed = ctClass.toBytecode();
            }
        } catch (Exception e) {
            System.err.println("Could not instrument  " + className + ",  exception : " + e.getMessage());
        } finally {
            if (ctClass != null) {
                ctClass.detach();
            }
        }
        return transformed;
    }

    private void AOPInsertMethod(CtMethod method) throws CannotCompileException {
        //situation 1:添加监控时间
        method.instrument(new ExprEditor() {
            public void edit(MethodCall m) throws CannotCompileException {
                m.replace("{ long stime = System.currentTimeMillis(); " +
                        "$_ = $proceed($$);" +
                        "System.out.println(\""
                        + m.getClassName() + "." + m.getMethodName()
                        + " cost:\" + (System.currentTimeMillis() - stime) + \" ms\");}");
            }
        });
        //situation 2:在方法体前后语句
        method.insertBefore("System.out.println(\"enter ===>> method\");");
        method.insertAfter("System.out.println(\"leave ===>> method\");");
    }

}